#pragma once

#ifndef __THREAD_H__
#define __THREAD_H__

#include <pthread.h>
#include <string>
#include <cstring>
#include <functional>
#include <cstdio>
#include <cassert>

class Thread; // 进行声明，因为C++找内容只会向上查找而不是全文查找

/**
 * 存储线程的上下文数据
 * 主要是因为start_routine为静态函数，若其想要使用类内的成员或函数，则这些成员或函数必须为静态的
 * 为了避免这种让多个成员都为静态的情况，就将上下文进行封装，然后作为参数给start_routine进行调用
 * 这样就可以避免static
*/
struct Context {
    Thread* _this;
    void* _args;

    Context(Thread* this_ = nullptr, void* args = nullptr)
        : _this(this_)
        , _args(args)
    {}
};

class Thread {
public:
    using func_t = std::function<void* (void*)>;

private:
    const int name_buffer_size_const = 128;

private:
    pthread_t _tid; // 线程的编号，给OS使用的
    std::string _name; // 线程的标识，给用户看的。 类似于inode和fd，key和shmid

    func_t _func; // 函数指针，或仿函数对象或lambda表达式
    void* _args; // 函数的参数

public:
    /**
     * @param func 线程的执行函数
     * @param args 执行函数的参数
     * @param num 线程的编号
    */
    Thread(func_t func, void* args, int num)
        : _func(func)
        , _args(args)
    {
        char name_buffer[name_buffer_size_const];
        snprintf(name_buffer, name_buffer_size_const, "thread - %d", num);
        _name = std::string(name_buffer);

        start(args);
    }

    Thread(const Thread& thread) = delete;
    Thread& operator=(const Thread& thread) = delete;
    Thread(Thread&& thread) = default;
    Thread& operator=(Thread&& thread) = default;
    ~Thread() = default;

public:
    void join() {
        int n = pthread_join(_tid, nullptr);
        assert(n == 0);
        (void)n;
    }

private:
    void* start(void* args) {
        Context context(this, args);
        int n = pthread_create(&_tid, nullptr, start_routine, (void*)&context);
        assert(n == 0);
        (void)n;
    }

    // 因为_func被包装器修饰，拥有C++的特性，所以不能像函数指针一样直接被C语言类型的系统接口所兼容
    // 因此封装了一层来执行函数
    void* run(void* args) {
        return _func(args);
    }

    // pthread_create中执行的start_routine必须由static修饰
    // 因为pthread_create要求start_routine中的参数有且只有一个，且必须是void*类型
    // 可是start_routine是成员函数，所以有一个隐藏的this指针
    // 为了消除这个this指针，所以必须用static修饰该函数
    // 因为静态函数是没有this指针的
    static void* start_routine(void* args) {
        Context* ptr_context = static_cast<Context*>(args);
        void* ret = ptr_context->_this->run(ptr_context->_args);
        return ret;
    }
};

#endif